#include <LengthUnit.h>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

typedef std::vector<const LengthUnit*> LengthUnitSeq;

#define INCH_TO_BASE_UNIT 1
#define FEET_TO_BASE_UNIT 12*INCH_TO_BASE_UNIT
#define YARD_TO_BASE_UNIT (3*FEET_TO_BASE_UNIT)
#define MILE_TO_BASE_UNIT (1760*YARD_TO_BASE_UNIT)

#define DEFINE_UNIT(unitname) \
const LengthUnit& \
LengthUnit::get##unitname() \
{\
    static const LengthUnit unit(unitname##_TO_BASE_UNIT,#unitname);\
    return unit;\
}

#define UNITSEQ getLengthUnitSeq()

namespace
{
    bool
    unitCompare(const LengthUnit* lhs, const LengthUnit* rhs)
    {
        return ((*lhs) > (*rhs));
    }

    void
    unitAppend(LengthUnitSeq& unitSeq, const LengthUnit& unit)
    {
        unitSeq.push_back(&unit);
        sort(unitSeq.begin(), unitSeq.end(), unitCompare);
        return;
    }

    LengthUnitSeq&
    getLengthUnitSeq()
    {
        static LengthUnitSeq unitSeq;

        if(unitSeq.empty())
        {
            unitAppend(unitSeq, MILE);
            unitAppend(unitSeq, YARD);
            unitAppend(unitSeq, FEET);
            unitAppend(unitSeq, INCH);

        }

        return unitSeq;
    }
}

DEFINE_UNIT(MILE)

DEFINE_UNIT(YARD)

DEFINE_UNIT(FEET)

DEFINE_UNIT(INCH)

const LengthUnit&
LengthUnit::getBaseUnit()
{
    return getINCH();
}

void
LengthUnit::vistorUnitSeq(LengthUnit::UnitSeqVistor& vistor)
{
    for(LengthUnitSeq::const_iterator iter = UNITSEQ.begin(); iter!=UNITSEQ.end(); iter++)
    {
        vistor.access(*iter);
    }
}


Amount
LengthUnit::convertToBaseUnit(const Amount& length) const
{
    return factor_m * length;
}

Amount
LengthUnit::convertFromBaseUnit(const Amount& amountInBase) const
{
    return amountInBase / factor_m;
}

Amount
LengthUnit::convertOddmentFromBaseUnit(const Amount& amountInBase) const
{
    return amountInBase % factor_m;
}

bool
LengthUnit::operator>(const LengthUnit& unit) const
{
    return factor_m > unit.factor_m;
}

LengthUnit::LengthUnit(const Amount& factor, const string& name) :
    factor_m(factor), name_m(name)
{
}

std::ostream&
operator<<(std::ostream& out, const LengthUnit& unit)
{
    out << unit.name_m;
    return out;
}

